package com.dongnam.quanly.web.system;

import java.lang.management.ManagementFactory;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Set;

import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.dongnam.quanly.common.persistence.HibernateUtil;
import com.dongnam.quanly.mbean.LRDConsoleApp;
import com.dongnam.quanly.mbean.LRDConsoleAppMBean;
import com.mchange.v2.c3p0.C3P0Registry;
import com.mchange.v2.c3p0.PooledDataSource;

public class LRDConsoleContextListener implements ServletContextListener {
	private static final Logger logger = LoggerFactory.getLogger(LRDConsoleContextListener.class);

	@Override
	public void contextInitialized(ServletContextEvent arg0) {
		logger.info("===================================");
		logger.info(String.format("LRD Web Console, Version %s", AppConfig.VERSION));
		logger.info("===================================");
		logger.info("Initializing application context...");

		try {
			loadConfig();
			HibernateUtil.init(AppConfig.getHibernateConfig());
			registerMBean();
		} catch (Exception e) {
			logger.error("Failed to initialise system context", e);
			// that'll block the app from running. container will call
			// contextDestroyed to cleanup afterwards
			throw new RuntimeException("contextInitialized failed. Adam fails to start.", e);
		}
	}

	@Override
	public void contextDestroyed(ServletContextEvent arg0) {
		logger.info("Application context being destroyed...");
		try {
			closeC3P0DataSources();
		} catch (Exception ignored) {
		}

		try {
			deregisterJDBC();
		} catch (Exception ignored) {
		}

		try {
			HibernateUtil.shutdown();
		} catch (Exception ignored) {
		}

		try {
			deregisterMBean();
		} catch (Exception ignored) {
		}

		try {
			logger.info("Sleep and wait for database shutdown by itself");
			// FIXME not a precise method!!
			// However, even if the thread is not killed before i awake, the
			// thread should finish it work
			// before JVM stops
			Thread.sleep(1000);
			logger.info("Wake up");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

	}

	private void loadConfig() {
		String configFilePath = System.getProperty("app.config", AppConfig.getDefaultAppConfigLocation());
		AppConfig.loadAppConfig(configFilePath);
		AppConfig.loadLogConfig();
	}

	private void registerMBean() throws Exception {
		MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();

		LRDConsoleAppMBean appMBean = new LRDConsoleApp();

		try {
			mbs.registerMBean(appMBean, new ObjectName(AppConfig.getManagementAppMBeanName()));
		} catch (Exception e) {
			logger.error("registerMBean failed", e);
			throw e;
		}
	}

	private void deregisterMBean() throws Exception {
		MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();

		try {
			mbs.unregisterMBean(new ObjectName(AppConfig.getManagementAppMBeanName()));
		} catch (Exception e) {
			logger.error("deregisterMBean failed");
		}
	}

	private void closeC3P0DataSources() {
		C3P0Registry.getNumPooledDataSources();

		@SuppressWarnings({ "unchecked", "rawtypes" })
		Iterator<Set> it = C3P0Registry.getPooledDataSources().iterator();
		while (it.hasNext()) {
			try {
				PooledDataSource dataSource = (PooledDataSource) it.next();
				dataSource.close();
			} catch (Exception e) {
				logger.error("", e);
			}
		}
	}

	private void deregisterJDBC() {
		Enumeration<Driver> drivers = DriverManager.getDrivers();
		Driver d = null;
		while (drivers.hasMoreElements()) {
			try {
				d = drivers.nextElement();
				DriverManager.deregisterDriver(d);
				logger.warn(String.format("Driver %s deregistered", d));
			} catch (SQLException ex) {
				logger.warn(String.format("Error deregistering driver %s", d), ex);
			}
		}
		Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
		Thread[] threadArray = threadSet.toArray(new Thread[threadSet.size()]);
		for (Thread t : threadArray) {
			if (t.getName().contains("Abandoned connection cleanup thread")) {
				synchronized (t) {
					t.stop(); // don't complain, it works
				}
			}
		}
	}

}
